Skip to content

feat(client): add microphone passthrough button to controls toolbar#620

Merged
m1k1o merged 3 commits intom1k1o:masterfrom
h1n054ur:feature/client-microphone-button
Feb 17, 2026
Merged

feat(client): add microphone passthrough button to controls toolbar#620
m1k1o merged 3 commits intom1k1o:masterfrom
h1n054ur:feature/client-microphone-button

Conversation

@h1n054ur
Copy link
Contributor

Summary

The neko v3 server supports microphone capture via WebRTC (capture.microphone.enabled defaults to true), but the legacy client bundled with the Docker images has no UI to enable it. This PR adds a microphone toggle button to the bottom controls toolbar.

Changes

  • client/src/neko/base.ts: Add enableMicrophone() / disableMicrophone() methods to BaseClient that:
    • Call navigator.mediaDevices.getUserMedia({ audio: true }) to capture the user's mic
    • Add the audio track to the existing RTCPeerConnection via addTrack()
    • Clean up (stop tracks, remove sender) on disable or disconnect
  • client/src/components/controls.vue: Add mic toggle button between play/pause and volume slider with:
    • fa-microphone / fa-microphone-slash icons
    • Tooltip with i18n strings
    • Error dialog via SweetAlert if mic access is denied
  • client/src/locale/en-us.ts: Add mic_on, mic_off, mic_error i18n strings

How it works

  1. User clicks the mic button in the toolbar
  2. Browser prompts for microphone permission
  3. Audio track is added to the peer connection, triggering onnegotiationneeded (already handled in base.ts)
  4. Server receives the audio track and pipes it into PulseAudio's virtual microphone
  5. Applications inside the container (e.g. Zoom web client, voice recorders) can use the virtual mic

Use case

This enables voice calls (Zoom, Google Meet, etc.) through the remote browser session — the user's physical mic audio is relayed into the container's virtual audio input device.

Testing

  • Client builds successfully (npm run build)
  • Tested manually with neko Google Chrome image behind a cloudflared tunnel with Cloudflare TURN relay
  • Mic audio successfully captured and played back through online voice recorder inside the remote session

h1n054ur and others added 2 commits February 11, 2026 22:20
Add mic toggle button to the bottom controls bar that enables users to
share their local microphone with the remote neko session via WebRTC.

The server already supports microphone capture (capture.microphone.enabled)
but the legacy client had no UI to trigger getUserMedia and send an audio
track to the peer connection.

Changes:
- base.ts: Add enableMicrophone/disableMicrophone methods that call
  getUserMedia and addTrack/removeTrack on the RTCPeerConnection.
  Mic is cleaned up automatically on disconnect.
- controls.vue: Add mic button (fa-microphone/fa-microphone-slash) between
  play/pause and volume controls with tooltip and error handling.
- en-us.ts: Add i18n strings for mic tooltips and error dialog.
@m1k1o
Copy link
Owner

m1k1o commented Feb 16, 2026

Thanks for the implementation. Although no features are planned for the current legacy system and there is no new one, I am considering adding at least check if the user is allowed to share his media (microhpone) and if the feature is even allowed on the system level.

Further more, it exposes the lack of control for the microphone. As one user sharing his audio can be easily overriden by another one doing that. And the GUI is not reflecting that. Since there is no currenst state for the user that would show who is currently "hosting the audio". Or it could be tied to the controls, so only person that is hosting can share their audio and when they stop hosting it's cleared. But it creates some complications for implicit hosting behaviour.

@h1n054ur
Copy link
Contributor Author

Hey, thanks for maintaining neko, been using it for a remote Zoom setup through a Cloudflare Tunnel and it's been solid. Appreciate the feedback on the PR and for pushing that io.EOF fix.

I went ahead and tied the mic to the active host, button only shows when you're controlling, auto-disables when you lose control, so no more conflicts with multiple users. That was a good call.

For the system-level and per-user permission checks (canShareMedia, capture.microphone.enabled), I looked into it but those aren't exposed to the legacy client through the translation layer, so there's not much I can do on the client side without server changes. The server already rejects the track if mic is disabled which is a decent fallback.

On a separate note, I've been poking around the v3 client branch, got it building and tested against the current server, seems to work well. Would love to help get it closer to production if you're open to that. What's still left to do there?

@m1k1o
Copy link
Owner

m1k1o commented Feb 17, 2026

I went ahead and tied the mic to the active host, button only shows when you're controlling, auto-disables when you lose control, so no more conflicts with multiple users. That was a good call.

Yeah, that should be enough without intrducing server changes to the legacy system.

What's still left to do there?

Actually the v3 client is now only a library with some debug UI that is similar to what we have right now. But I was considering giving up on Vue and switching to React, because of the breaking change of Vue2->Vue3. I can't trust it anymore, that we migrate everything and there might be Vue4 compeltly depecating our whole codebase, again.

@m1k1o m1k1o merged commit bc19ab8 into m1k1o:master Feb 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants